#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include "commonapi.h"
#include "protocal.h"
#include "psm.h"

#define MAXMSG 1024
#define STACK_SIZE_SERVER 4096
#define MAX_TCP_CLIENT 6

static const char* TAG = "tcpserver";

typedef struct
{
   int socket;
   TickType_t last_communitcation_time;
} user_info_t;

typedef struct
{
   uint8_t total_users;
   user_info_t user_info[MAX_TCP_CLIENT];
} socket_server_t;

static StaticTask_t xTaskBuffer_server;
static StackType_t xStack_server[STACK_SIZE_SERVER];
static socket_server_t server;
static fd_set active_fd_set, read_fd_set;
static TaskHandle_t xHandle_tcpserver = NULL;
static uint8_t socket_server_status;

static bool tcpserver_user_update(int socket)
{
   uint8_t i;
   for (i = 0; i < MAX_TCP_CLIENT; i++)
   {
      if (server.user_info[i].socket == socket)
      {
         server.user_info[i].last_communitcation_time = xTaskGetTickCount();
         return true;
      }
   }
   ESP_LOGE(TAG, "update failed, socket not find\n");
   return false;
}

static bool tcpserver_add_user(int socket)
{
   uint8_t i;
   if (server.total_users < MAX_TCP_CLIENT)
   {
      for (i = 0; i < MAX_TCP_CLIENT; i++)
      {
         if (server.user_info[i].socket == 0)
         {
            server.user_info[i].socket = socket;
            server.user_info[i].last_communitcation_time = xTaskGetTickCount();
            server.total_users++;
            return true;
         }
      }
   }
   ESP_LOGE(TAG, "tcp connection full\n");
   return false;
}

static void tcpserver_connection_timeout_monitor(void)
{
   uint8_t i;
   if (server.total_users < MAX_TCP_CLIENT)
   {
      for (i = 0; i < MAX_TCP_CLIENT; i++)
      {
         if (server.user_info[i].socket != 0)
         {
            if (server.user_info[i].last_communitcation_time + 15*60*1000 / portTICK_PERIOD_MS <= xTaskGetTickCount())
            {
               ESP_LOGI(TAG, "close %d:\n", server.user_info[i].socket);
               close(server.user_info[i].socket);
               FD_CLR(server.user_info[i].socket, &active_fd_set);
               server.user_info[i].socket = 0;
               server.user_info[i].last_communitcation_time = 0;
               server.total_users--;
            }
         }
      }
   }
}

static bool tcpserver_delete_user(int socket)
{
   uint8_t i;
   if (server.total_users > 0)
   {
      for (i = 0; i < MAX_TCP_CLIENT; i++)
      {
         if (server.user_info[i].socket == socket)
         {
            server.user_info[i].socket = 0;
            server.user_info[i].last_communitcation_time = 0;
            server.total_users--;
            return true;
         }
      }
   }
   ESP_LOGE(TAG, "del failed, socket not find\n");
   return false;
}

// static int read_from_client(int filedes)
// {
//    char buffer[MAXMSG];
//    int nbytes;

//    // Clear buffer and read fd
//    memset(buffer, 0, MAXMSG);
//    nbytes = read(filedes, buffer, MAXMSG);
//    if (nbytes < 0)
//    {
//       ESP_LOGE(TAG, "read err: %d\n", errno);
//       return -1;
//    }
//    else
//    {
//       return nbytes;
//    }
// }

static void tcpserver(void* p)
{
   int sock = 0;
   struct sockaddr_in clientname;
   struct sockaddr_in servername;
   socklen_t size;
   int i;
   int ret;

   server.total_users = 0;

   // Create socket and listen for connections

   while (1)
   {
      switch (socket_server_status)
      {
      case 0:
         sock = socket(AF_INET, SOCK_STREAM, 0);
         if (sock < 0)
         {
            ESP_LOGE(TAG, "create socket error: %d\n", errno);
            while (1)
               ;
         }
         servername.sin_family = AF_INET;
         servername.sin_port = htons(8888);
         servername.sin_addr.s_addr = htonl(INADDR_ANY);

         if (bind(sock, (struct sockaddr*)&servername, sizeof(servername)) < 0)
         {
            ESP_LOGE(TAG, "bind error: %d\n", errno);
            while (1)
               ;
         }

         if (listen(sock, 6) < 0)
         {
            ESP_LOGE(TAG, "listen error: %d\n", errno);
            while (1)
               ;
         }
         // Init set of active sockets
         FD_ZERO(&active_fd_set);
         FD_SET(sock, &active_fd_set);
         socket_server_status = 1;
         break;
      case 1:
         if (sock < 3)
         {
            socket_server_status = 0;
         }
         read_fd_set = active_fd_set;

         // Block until input arrives on active socket
         struct timeval tv = { 0 };
         tv.tv_usec = 20000;
         tcpserver_connection_timeout_monitor();
         ret = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &tv);
         if (ret < 0)
         {
            ESP_LOGE(TAG, "select error: %d\n", errno);
         }
         else if (ret > 0)
         {
            // service all sockets with input pending
            for (i = 0; i < FD_SETSIZE; i++)
            {
               if (FD_ISSET(i, &read_fd_set))
               {
                  if (i == sock)
                  {
                     // Connection request on original socket
                     int new;
                     size = sizeof(clientname);
                     new = accept(sock, (struct sockaddr*)&clientname, &size);

                     if (new < 0)
                     {
                        ESP_LOGE(TAG, "accept error: %d\n", errno);
                     }
                     else
                     {
                        //server.total_users++;
                        char* host = inet_ntoa(clientname.sin_addr);
                        unsigned int port = ntohs(clientname.sin_port);
                        tcpserver_add_user(new);

                        ESP_LOGI(TAG, "Connect from %s, port %d, total users: %d.", host, port, server.total_users);

                        // Set fd
                        FD_SET(new, &active_fd_set);
                     }
                  }
                  else
                  {
                     uint8_t buffer[MAXMSG];
                     int nbytes;

                     // Clear buffer and read fd
                     nbytes = read(i, buffer, MAXMSG);
                     // Data arriving on an already connected socket
                     if (nbytes < 0)
                     {
                        //free(server.users[server.total_users]);
                        ESP_LOGE(TAG, "recv error\n");
                        ESP_LOGI(TAG, "close fd:%d\n", i);
                        tcpserver_delete_user(i);
                        close(i);
                        FD_CLR(i, &active_fd_set);
                     }
                     else if (nbytes > 0)
                     {
                        uint8_t send_buff[32] = "";
                        uint8_t send_data_len = 0;
                        // ESP_LOGI(TAG, "rx: ");
                        // printhex(buffer, nbytes);
                        protocal_server_rx_handle(buffer, nbytes, send_buff, &send_data_len);
                        tcpserver_user_update(i);
                        nbytes = send(i, send_buff, send_data_len, 0);
                        if (nbytes <= 0)
                        {
                           close(i);
                           ESP_LOGE(TAG, "send faild\n");
                           ESP_LOGI(TAG, "close fd:%d\n", i);
                        }
                     }
                  }
               }
            }
         }
         if (psm_get_status() == PM_CHRAGING || psm_get_status() == PM_GOTO_SLEEP)
         {
            if (server.total_users == 0)
            {
               if (sock != 0)
               {
                  ESP_LOGI(TAG, "close socket\n");
                  close(sock);
                  sock = 0;
                  socket_server_status = 2;
                  vTaskSuspend(NULL);

               }
            }
         }
         break;
      }
   }
}
void tcp_server_init(void)
{
   // Create the task without using any dynamic memory allocation.
   xHandle_tcpserver = xTaskCreateStatic(
      tcpserver,            // Function that implements the task.
      "tcpserver",          // Text name for the task.
      STACK_SIZE_SERVER,    // Stack size in bytes, not words.
      (void*)NULL,         // Parameter passed into the task.
      tskIDLE_PRIORITY + 1, // Priority at which the task is created.
      xStack_server,        // Array to use as the task's stack.
      &xTaskBuffer_server); // Variable to hold the task's data structure.
   if (xHandle_tcpserver == NULL)
   {
      ESP_LOGE(TAG, "Create tcpserver task fail\n");
   }
}

bool tcp_all_client_close(void)
{
   return server.total_users == 0;
}

bool tcp_server_is_suspend(void)
{
   return socket_server_status == 2;
}

void tcp_server_suspend(void)
{
   vTaskSuspend(xHandle_tcpserver);
}